/*
 * Copyright (C) 2010 by Joseph A. Marrero and Shrewd LLC. http://www.manvscode.com/
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "hash-table.h"
			
#define TEST_REHASH

#if defined(TEST_REHASH)
#define LOAD_FACTOR       (0.7)
#endif

size_t  ip_hash    ( const void *data );
boolean ip_destroy ( void *data );

static const char *ips[] = {
	"85.162.151.163", "252.241.190.83", "82.107.121.249", "226.233.19.20",
	"31.142.81.45", "39.87.8.86", "208.212.5.167", "117.119.130.82",
	"237.74.153.27", "82.106.61.88", "74.36.213.54", "149.173.142.104",
	"181.53.60.95", "108.221.140.196", "226.61.50.17", "207.89.180.180",
	"39.61.35.206", "150.249.167.223", "102.224.30.252", "140.123.14.44",
	"143.66.48.202", "206.123.159.188", "135.177.184.209", "187.214.138.236",
	"14.99.21.46", "58.164.220.60", "167.32.6.122", "221.52.27.147",
	"136.236.245.76", "104.214.104.150", "142.111.9.160", "22.23.1.91",
	"171.34.1.123", "65.201.71.147", "151.207.172.219", "5.81.243.137",
	"14.44.113.121", "89.107.96.13", "108.225.89.213", "99.85.185.193",
	"85.113.68.66", "230.151.186.11", "113.254.103.104", "85.118.80.91",
	"79.99.34.194", "216.168.206.2", "106.69.10.167", "133.77.26.67",
	"207.88.96.231", "4.194.100.151", "166.154.104.156", "153.245.92.200",
	"13.189.155.242", "224.202.34.0", "44.45.16.176", "185.227.222.82",
	"214.137.60.190", "188.218.77.160", "125.99.245.53", "196.24.216.209",
	"244.209.85.117", "41.213.29.119", "127.85.130.45", "160.58.184.224",
	"148.119.67.116", "69.81.209.16", "26.67.52.199", "3.222.91.14",
	"107.2.204.203", "1.160.28.96", "101.142.102.237", "107.20.255.41",
	"107.20.34.204", "107.3.242.141",  "107.3.72.200", "108.0.40.239",
	"108.14.101.43", "108.14.82.30", "108.18.101.109", "108.197.198.49",
	"108.2.192.183", "108.202.198.56", "108.23.216.187", "108.40.37.82",
	"108.41.243.205", "108.60.198.111", "108.75.253.230", "108.83.201.6",
	"108.86.221.70", "109.107.35.128", "109.107.35.154", "109.11.154.122",
	"109.11.7.186", "109.130.182.232", "109.151.251.174", "109.169.23.202",
	"109.169.41.234", "109.169.46.53", "109.169.60.2", "109.191.72.18",
	"109.192.105.0", "109.193.162.168", "109.193.164.44", "109.193.228.3",
	"109.193.58.78", "109.197.193.160", "109.201.131.11", "109.201.77.193",
	"109.201.84.180", "109.209.106.162", "109.210.135.178", "109.210.24.21",
	"109.227.121.243", "109.233.120.99", "109.234.82.101", "109.235.50.234",
	"109.236.85.157", "109.74.146.9", "109.74.195.194", "109.74.196.114",
	"109.74.196.149", "109.74.198.73", "109.74.206.12", "109.75.178.93",
	"109.88.51.52", "110.12.143.148", "110.174.188.183", "110.76.98.149",
	"111.118.177.101", "111.89.168.73", "112.213.105.78", "113.146.94.137",
	"113.212.97.156", "114.142.154.166", "114.189.241.200", "114.31.211.12",
	"114.31.211.29", "115.134.68.212", "115.186.170.209", "115.64.76.83",
	"115.84.182.227", "116.71.2.165", "116.86.238.119", "117.18.75.235",
	"118.104.175.20", "118.172.60.124", "118.208.206.75", "119.202.92.41",
	"119.246.31.160", "119.42.144.18", "12.174.113.200", "12.174.240.138",
	"120.50.40.184", "120.56.161.135", "120.56.165.228", "121.210.37.236",
	"121.73.248.43", "121.98.132.192", "122.116.16.69", "122.133.95.63",
	"122.155.3.145", "122.249.117.228", "122.26.202.31", "122.61.134.249",
	"123.108.108.147", "123.203.44.177", "123.243.54.50", "124.157.100.138",
	"124.188.0.138", "124.197.16.200", "124.217.225.212", "124.217.248.251",
	"124.217.250.192", "124.217.253.204", "124.41.66.220", "124.85.38.4",
	"125.103.83.68", "125.194.116.193", "125.54.166.143", "128.111.48.31",
	"128.117.43.92", "128.173.55.28", "128.173.89.245", "128.206.14.126",
	"128.232.18.57", "128.233.94.197", "128.31.0.34", "128.32.153.66",
	"128.59.16.164", "128.6.224.107", "129.133.8.19", "129.187.150.131",
	"129.25.11.27", "129.97.74.17", "129.97.81.127", "130.184.75.51",
	"130.243.230.96", "131.128.160.244", "131.130.199.36", "131.215.176.98",
	"132.187.9.81", "132.187.9.83", "132.187.9.86", "132.230.150.81",
	"134.106.99.126", "134.147.198.10", "134.169.86.54", "134.34.147.22",
	"134.99.112.165", "134.99.112.170", "137.56.163.46", "137.56.163.64",
	"138.100.10.206", "138.100.10.209", "140.116.157.180", "140.186.70.48",
	"140.192.218.139", "141.105.66.117", "141.156.235.53", "141.219.155.230",
	"141.30.218.4", "141.70.120.13", "142.244.137.57", "146.115.176.148",
	"147.46.234.176", "147.52.17.48", "147.52.74.129", "147.83.175.49",
	"149.20.54.45", "149.9.0.57", "149.9.0.58", "149.9.0.59",
	"149.9.0.60", "150.206.2.101", "150.244.21.96", "151.33.51.158",
	"151.60.193.230", "151.95.224.77", "152.13.224.3", "156.34.88.49",
	"158.250.17.73", "158.42.129.205", "161.53.160.104", "161.53.29.203",
	"166.70.15.14", "166.70.81.17", "168.150.251.39", "173.10.120.206",
	"173.11.153.225", "173.11.21.225", "173.11.57.243", "173.13.141.30",
	"173.16.79.84", "173.160.180.189", "173.160.45.211", "173.162.144.179",
	"173.163.234.185", "173.164.139.195", "173.164.222.105", "173.193.20.168",
	"173.193.226.35", "173.2.77.159", "173.203.105.144", "173.208.132.210",
	"173.208.164.18", "173.212.200.141", "173.212.217.212", "173.212.255.150",
	"173.228.85.139", "173.230.128.8", "173.230.152.102", "173.230.152.218",
	"173.230.154.16", "173.230.154.90", "173.230.156.36", "173.231.56.167",
	"173.236.66.251", "173.238.232.237", "173.242.116.19", "173.242.118.116",
	"173.244.180.173", "173.245.73.101", "173.246.102.147", "173.246.103.93",
	"173.25.240.115", "173.254.192.36", "173.254.192.37", "173.254.192.38",
	"173.254.216.66", "173.254.216.67", "173.254.216.68", "173.254.216.69",
	"173.255.193.216", "173.255.205.176", "173.255.208.174", "173.255.209.181",
	"173.255.211.175", "173.255.211.187", "173.255.216.59", "173.255.220.202",
	"173.255.228.169", "173.255.231.9", "173.255.238.178", "173.255.246.16",
	"173.255.246.162", "173.255.251.207", "173.26.76.58", "173.48.18.88",
	"173.50.88.145", "173.55.47.250", "173.56.96.253", "173.57.170.197",
	"173.57.91.176", "173.58.188.67", "173.66.182.151", "173.73.52.122",
	"173.78.25.37", "173.8.11.58", "173.87.78.196", "173.9.115.13",
	"174.100.254.236", "174.101.0.195", "174.129.103.134", "174.129.19.107",
	"174.129.2.232", "174.136.103.74", "174.138.163.86", "174.138.169.222",
	"174.140.163.93", "174.140.166.226", "174.141.11.77", "174.142.75.26",
	"174.143.243.243", "174.24.147.69", "174.30.138.55", "174.34.146.248",
	"174.37.99.107", "174.45.181.206", "174.61.177.67", "174.64.14.31",
	"174.92.185.240", "175.116.237.244", "175.138.215.132", "175.41.142.121",
	"175.45.25.44", "178.118.236.99", "178.140.42.216", "178.142.92.22",
	"178.16.39.133", "178.162.166.13", "178.162.191.51", "178.18.18.140",
	"178.187.78.26", "178.200.225.161", "178.202.102.34", "178.203.132.133",
	"178.21.20.86", "178.211.28.46", "178.24.230.24", "178.25.89.6",
	"178.250.170.55", "178.251.144.152", "178.26.10.123", "178.3.130.124",
	"178.32.98.42", "178.33.107.194", "178.33.140.154", "178.33.156.180",
	"178.33.32.123", "178.36.12.200", "178.47.83.137", "178.52.191.211",
	"178.52.98.211", "178.61.37.58", "178.63.15.200", "178.63.157.82",
	"178.63.16.48", "178.63.246.164", "178.63.252.51", "178.63.66.212",
	"178.63.66.76", "178.63.78.8", "178.63.89.71", "178.63.95.70",
	"178.63.96.79", "178.7.165.129", "178.73.217.178", "178.73.217.85",
	"178.73.218.107", "178.73.218.64", "178.73.218.78", "178.73.42.176",
	"178.74.1.96", "178.74.100.222", "178.75.134.140", "178.77.102.213",
	"178.77.77.19", "178.77.78.173", "178.79.136.230", "178.79.136.87",
	"178.79.141.108", "178.79.144.120", "178.79.147.117", "178.79.149.162",
	"178.79.170.122", "178.79.179.37", "178.79.180.47", "178.8.120.154",
	"178.93.189.79", "18.181.2.107", "18.187.1.68", "180.0.50.215",
	"180.14.113.44", "180.149.96.69", "180.219.78.182", "182.55.161.241",
	"183.82.59.46", "184.100.189.157", "184.105.220.24", "184.105.231.11",
	"184.105.237.85", "184.106.71.154", "184.106.92.236", "184.107.145.245",
	"184.154.13.11", "184.154.169.71", "184.172.20.159", "184.18.151.21",
	"184.22.219.147", "184.22.219.163", "184.22.231.67", "184.22.241.118",
	"184.39.159.78", "184.45.26.146", "184.58.15.210", "184.61.171.15",
	"184.72.106.52", "184.72.156.44", "184.82.19.30", "184.95.38.205",
	"186.18.24.221", "186.182.8.61", "186.212.14.76", "186.214.50.79",
	"187.54.112.223", "187.54.96.225", "187.55.194.177", "188.100.181.57",
	"188.101.187.195", "188.104.133.206", "188.104.136.148", "188.108.160.141",
	"188.114.200.221", "188.116.4.62", "188.118.196.40", "188.118.215.149",
	"188.124.19.114", "188.126.75.253", "188.134.16.230", "188.134.30.144",
	"188.134.4.177", "188.134.74.183", "188.138.104.156", "188.138.107.133",
	"188.138.113.25", "188.138.113.26", "188.138.82.143", "188.138.88.130",
	"188.138.91.232", "188.143.233.136", "188.143.31.166", "188.165.170.196",
	"188.165.199.8", "188.165.217.106", "188.165.26.254", "188.165.39.224",
	"188.165.46.24", "188.165.47.11", "188.175.124.11", "188.186.13.243",
	"188.187.159.99", "188.187.168.91", "188.192.248.229", "188.194.195.112",
	"188.40.116.253", "188.40.12.93", "188.40.126.20", "188.40.128.246",
	"188.40.137.161", "188.40.166.29", "188.40.178.5", "188.40.20.202",
	"188.40.200.50", "188.40.206.42", "188.40.209.99", "188.40.248.138",
	NULL
};

int main( int argc, char *argv[] )
{
	boolean      result;
	hash_table_t table;
	int          i;

	#if defined(USE_ALLOCATORS)
	hash_table_create( &table, 1, ip_hash, ip_destroy, (hash_table_compare_function) strcmp, malloc, free );
	#else
	hash_table_create( &table, 1, ip_hash, ip_destroy, (hash_table_compare_function) strcmp );
	#endif


	srand( 0 );


	for( i = 0; ips[ i ]; i++ )
	{
		const char *ip = ips[ i ];

		result = hash_table_insert( &table, strdup(ip) );
		assert( result );
		printf( "   Added (%03d): %-16s      %4.1lf  (%ld)", i, ip, hash_table_load_factor(&table), hash_table_size(&table) );
	
		#ifdef TEST_REHASH
		if( hash_table_rehash( &table, LOAD_FACTOR ) )
		{
			printf( " ---> Rehashed (size = %ld, table_size = %ld)", hash_table_size(&table), hash_table_table_size(&table) );
		}
		#endif

		printf( "\n" );
	}

	for( i = 0; ips[i]; i++ )
	{
		void *found_ip = NULL;

		if( hash_table_find( &table, ips[ i ], &found_ip ) )
		{
			char ip[ 16 ];
			strcpy( ip, found_ip );

			result = hash_table_remove( &table, found_ip );
			assert( result );
			printf( " Removed (%03d): %-16s      %4.1lf  (%ld)", i, ip, hash_table_load_factor(&table), hash_table_size(&table) );

			#ifdef TEST_REHASH
			if( hash_table_rehash( &table, LOAD_FACTOR ) )
			{
				printf( " ---> Rehashed (size = %ld, table_size = %ld)", hash_table_size(&table), hash_table_table_size(&table) );
			}
			#endif

			printf( "\n" );
		}
	}

	printf( "size = %ld, table-size = %ld\n", hash_table_size(&table), hash_table_table_size(&table) );

	hash_table_destroy( &table );
	return 0;
}

size_t ip_hash( const void *data )
{
	unsigned short count = 0;
	size_t hash          = 0;

	assert( data );

	char ip[ 24 ];
	strncpy( ip, data, sizeof(ip) );
	ip[ sizeof(ip) - 1 ] = '\0';

	char *token = strtok( (char *) ip, "." );

	while( token != NULL )
	{
		int part = atoi( token );

		hash |= (part << 8 * count);
	
		token = strtok( NULL, "." );
	}

	return hash;
}

boolean ip_destroy( void *data )
{
	free( data );
	return TRUE;
}
